//
//  ZHNetworkingManager.m
//  ToolsEnginer
//
//  Created by 正合适 on 2017/6/2.
//  Copyright © 2017年 正合适. All rights reserved.
//  公用的网络请求模块

/****请勿在该类里面嵌入业务代码，通过继承该类或者其他方式创建项目的网络请求类****/

#import "ZHNetworkingManager.h"
#import "AFNetworking.h"
#import "ZHCache.h"
#import "ZHNetworkingCacheModel.h"
#import "MJExtension.h"
#define kRequestTimeOut    15
#define SAFE_BLOCK(A,...) if(A){A(__VA_ARGS__);}
#define kCacheTable         @"ZHNetworkingManagerCache"

#define kUrlError            @"请求失败(-00001)"
#define kRequestTimedOut     @"请求超时，请稍后重试"              // -1001
#define kNetworkException    @"网络连接失败，请检查您的网络设置"    // 网络不通
#define kServerException     @"网络异常，请稍后重试"              // 网络通，但网络层报错

// 请求方法枚举
typedef NS_ENUM(NSUInteger, RequestMethodEnum) {
    Request_Method_Get = 1,
    Request_Method_Post = 2,
    Request_Method_Put = 3,
    Request_Method_PutToken= 4,
};

@interface ZHNetworkingManager ()

/**af网络管理*/
@property (nonatomic, strong) AFHTTPSessionManager *manager;
/**网络装填管理类*/
@property (nonatomic, strong) AFNetworkReachabilityManager *netManager;
/**网络超时时间 小于等于0：使用默认超时时间*/
@property (nonatomic, assign) NSTimeInterval requestTimeOut;
/**请求头参数*/
@property (nonatomic, strong) NSMutableDictionary *headerParams;

@end

@implementation ZHNetworkingManager
#pragma mark - ---------- private method ----------
#pragma mark - 单例
+ (instancetype)instance {
    static ZHNetworkingManager *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[ZHNetworkingManager alloc] init];
    });
    return instance;
}

#pragma mark - 初始化
- (instancetype)init {
    self = [super init];
    if (self) {
        _manager = [AFHTTPSessionManager manager];
        _manager.responseSerializer = [AFHTTPResponseSerializer serializer];
        _manager.requestSerializer = [AFHTTPRequestSerializer serializer];
        _netManager = [AFNetworkReachabilityManager manager];
        _requestTimeOut = -1;
    }
    return self;
}

#pragma mark - 设置请求超时时间
- (void)httpRequestTimeOut {
    if (self.configuration && [self.configuration respondsToSelector:@selector(configRequestTimeout)]) {
        [ZHNetworkingManager instance].requestTimeOut = [self.configuration configRequestTimeout];
    }
    [_manager.requestSerializer willChangeValueForKey:@"timeoutInterval"];
    _manager.requestSerializer.timeoutInterval = [ZHNetworkingManager instance].requestTimeOut <= 0 ? kRequestTimeOut : [ZHNetworkingManager instance].requestTimeOut;
    [_manager.requestSerializer didChangeValueForKey:@"timeoutInterval"];
}

#pragma mark - 请求错误分析
- (NSString *)requestErrorSerializer:(NSError *)error {
    if (error.code == -1001) {
        return kRequestTimedOut;
    }
    if (error.code == -1009) {
        return kNetworkException;
    }
    return [NSString stringWithFormat:@"%@(%ld)", kServerException, (long)error.code];
}

#pragma mark - 缓存数据
- (void)cacheWith:(NSString *)key cacheDays:(CGFloat)cacheDays val:(NSString *)val {
    if (cacheDays >= 0 && key.length > 0) {
        // 0为永久缓存 大于0为使用缓存
        CGFloat expireTime = cacheDays == 0? 0:cacheDays * 60 * 60*24;
        ZHNetworkingCacheModel *cacheModel = [[ZHNetworkingCacheModel alloc] init];
        cacheModel.saveTime = [ZHNetworkingCacheModel currentTime];
        cacheModel.expireTime = expireTime;
        cacheModel.jsonCache = val;
        NSString *jsonStr = [cacheModel mj_JSONString];
        [ZHCache cacheWithKey:key value:jsonStr table:kCacheTable];
    }
}

#pragma mark - 拼接底层参数
- (NSMutableDictionary *)configBaseParamsWithOrgionParams:(NSDictionary *)params {
    NSMutableDictionary *dic = [params mutableCopy];
    NSDictionary *baseParams;
    if (self.configuration && [self.configuration respondsToSelector:@selector(configBaseRequestParams)]) {
        baseParams = [self.configuration configBaseRequestParams];
        if (baseParams && baseParams.allKeys.count > 0) {
            for (NSInteger i = 0; i < baseParams.allKeys.count; i ++) {
                NSString *key = baseParams.allKeys[i];
                if (![dic.allKeys containsObject:key]) {
                    [dic setObject:baseParams[key] forKey:key];
                }
            }
        }
    }
    return dic;
}

#pragma mark - 请求头添加字段
- (void)configRequestHeader {
    if (self.configuration && [self.configuration respondsToSelector:@selector(configHeader)]) {
        NSDictionary *dic = [self.configuration configHeader];
        if (dic) {
            if (dic.allKeys.count > 0) {
                for (NSInteger i = 0; i < dic.allKeys.count; i ++) {
                    NSString *key = dic.allKeys[i];
                    [_manager.requestSerializer setValue:dic[key] forHTTPHeaderField:key];
                }
            }
        }
    }
}

#pragma mark -  根据项目具体情况，可以选择在这里解析数据，判断状态码，并做相应的操作【主要考虑token】
- (BOOL)serilizerCodeWith:(NSInteger)code {
    if (self.configuration && [self.configuration respondsToSelector:@selector(serilizerErrorCodeWith:)]) {
        return [self.configuration serilizerErrorCodeWith:code];
    } else {
        return NO;
    }
}

#pragma mark - get请求base
- (void)get:(NSString *)url params:(NSDictionary *)params cacheTimeOut:(CGFloat)cacheDays cacheKey:(NSString *)cacheKey success:(SuccessBlock)successBlock error:(ErorrBlock)errorBlock {
    __weak typeof(self) weakSelf = self;
    
    [_manager GET:url parameters:params progress:^(NSProgress * _Nonnull downloadProgress) {
        
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSString *responseJson = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
        // 根据项目具体情况，可以选择在这里解析数据，判断token，并做相应的操作
        if (responseJson.length > 0) {
            [weakSelf cacheWith:cacheKey cacheDays:cacheDays val:responseJson];
        }
        SAFE_BLOCK(successBlock,responseJson);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
        if ([self serilizerCodeWith:response.statusCode]) return;
        SAFE_BLOCK(errorBlock, [self requestErrorSerializer:error]);
    }];
}

#pragma mark - post请求base
- (void)post:(NSString *)url params:(NSDictionary *)params cacheTimeOut:(CGFloat)cacheDays cacheKey:(NSString *)cacheKey success:(SuccessBlock)successBlock error:(ErorrBlock)errorBlock {
    __weak typeof(self) weakSelf = self;
    [_manager POST:url parameters:params progress:^(NSProgress * _Nonnull uploadProgress) {
        
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSString *responseJson = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
        if (responseJson.length > 0) {
            [weakSelf cacheWith:cacheKey cacheDays:cacheDays val:responseJson];
        }
        SAFE_BLOCK(successBlock,responseJson);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
        if ([self serilizerCodeWith:response.statusCode]) return;
        SAFE_BLOCK(errorBlock, [self requestErrorSerializer:error]);
    }];
}

#pragma mark - put请求base
- (void)put:(NSString *)url params:(NSDictionary *)params cacheTimeOut:(CGFloat)cacheDays cacheKey:(NSString *)cacheKey success:(SuccessBlock)successBlock error:(ErorrBlock)errorBlock {
    __weak typeof(self) weakSelf = self;
    [_manager PUT:url parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSString *responseJson = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
        if (responseJson.length > 0) {
            [weakSelf cacheWith:cacheKey cacheDays:cacheDays val:responseJson];
        }
        SAFE_BLOCK(successBlock,responseJson);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
        if ([self serilizerCodeWith:response.statusCode]) return;
        SAFE_BLOCK(errorBlock, [self requestErrorSerializer:error]);
    }];
}

#pragma mark - http请求
- (void)requestWith:(RequestMethodEnum)method url:(NSString *)url params:(NSDictionary *)params cacheTimeOut:(CGFloat)cacheDays cacheKey:(NSString *)cacheKey success:(SuccessBlock)successBlock error:(ErorrBlock)errorBlock cache:(CacheBlock)cacheBlock {
    if (url.length == 0) {
        SAFE_BLOCK(errorBlock,kUrlError);
        return;
    }
    // 设置超时
    [self httpRequestTimeOut];
    
    // 缓存判断
    if (cacheBlock && cacheDays >= 0 && cacheKey.length > 0) {
        // 首先调用缓存数据
        NSString *jsonCache = [ZHCache fromCacheWithKey:cacheKey table:kCacheTable];
        ZHNetworkingCacheModel *cacheModel = [ZHNetworkingCacheModel mj_objectWithKeyValues:jsonCache];
        if ([cacheModel isExpire]) {
            // 缓存过期
            [ZHCache deleteCacheWithKey:cacheKey table:kCacheTable];
        } else if (![cacheModel isExpire] && cacheModel.jsonCache.length > 0) {
            SAFE_BLOCK(cacheBlock, cacheModel.jsonCache);
        }
    }
    // 设置请求头
    [self configRequestHeader];
    __weak typeof(self) weakSelf = self;
    switch (method) {
        case Request_Method_Get:
            [weakSelf get:url params:[self configBaseParamsWithOrgionParams:params] cacheTimeOut:cacheDays cacheKey:cacheKey success:successBlock error:errorBlock];
            break;
        case Request_Method_Post:
            [weakSelf post:url params:[self configBaseParamsWithOrgionParams:params] cacheTimeOut:cacheDays cacheKey:cacheKey success:successBlock error:errorBlock];
            break;
        case Request_Method_Put:
            [weakSelf put:url params:[self configBaseParamsWithOrgionParams:params] cacheTimeOut:cacheDays cacheKey:cacheKey success:successBlock error:errorBlock];
        default:
            break;
    }
}

#pragma mark - ---------- public method ----------

#pragma mark - 网络状态监听开启
+ (void)networkingStatusObserver:(BOOL)start {
    if (start) {
        [[ZHNetworkingManager instance].netManager startMonitoring];
    } else {
        [[ZHNetworkingManager instance].netManager stopMonitoring];
    }
}

#pragma mark - 网络状态变化
+ (void)networkingChangeStatus:(NetworkingStatusBlock)networkingStatusBlock {
    [[ZHNetworkingManager instance].netManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        // 当网络状态改变时调用
        switch (status) {
            case AFNetworkReachabilityStatusUnknown:
                NSLog(@"未知网络");
                SAFE_BLOCK(networkingStatusBlock,NetworkingStatusUnknown);
                break;
            case AFNetworkReachabilityStatusNotReachable:
                NSLog(@"没有网络");
                SAFE_BLOCK(networkingStatusBlock,NetworkingStatusNotReachable);
                break;
            case AFNetworkReachabilityStatusReachableViaWWAN:
                NSLog(@"手机自带网络");
                SAFE_BLOCK(networkingStatusBlock,NetworkingStatusViaWWAN);
                break;
            case AFNetworkReachabilityStatusReachableViaWiFi:
                NSLog(@"WIFI");
                SAFE_BLOCK(networkingStatusBlock,NetworkingStatusViaWiFi);
                break;
        }
    }];
}

#pragma mark - 网络是否可用 wifi 手机
+ (BOOL)isNetReachable {
    return [[ZHNetworkingManager instance].netManager isReachable];
}

#pragma mark - WIFI是否可用
+ (BOOL)isWifiReachable {
    return [[ZHNetworkingManager instance].netManager isReachableViaWiFi];
}

#pragma mark - 手机网络是否可用
+ (BOOL)isViaWWANReachable {
    return [[ZHNetworkingManager instance].netManager isReachableViaWWAN];
}

#pragma mark - 【GET】文本数据请求-使用缓存
+ (void)get:(NSString *)url params:(NSDictionary *)params cacheTimeOut:(float)cacheDays cacheKey:(NSString *)cacheKey success:(SuccessBlock)successBlock error:(ErorrBlock)errorBlock cache:(CacheBlock)cacheBlock {
    [[ZHNetworkingManager instance] requestWith:Request_Method_Get url:url params:params cacheTimeOut:cacheDays cacheKey:cacheKey success:successBlock error:errorBlock cache:cacheBlock];
}

#pragma mark - 【GET】文本数据请求-不使用缓存
+ (void)get:(NSString *)url params:(NSDictionary *)params success:(SuccessBlock)successBlock error:(ErorrBlock)errorBlock {
    [[ZHNetworkingManager instance] requestWith:Request_Method_Get url:url params:params cacheTimeOut:-1 cacheKey:nil success:successBlock error:errorBlock cache:nil];
}

#pragma mark - 【POST】文本数据请求-使用缓存
+ (void)post:(NSString *)url params:(NSDictionary *)params cacheTimeOut:(float)cacheDays cacheKey:(NSString *)cacheKey success:(SuccessBlock)successBlock error:(ErorrBlock)errorBlock cache:(CacheBlock)cacheBlock {
    [[ZHNetworkingManager instance] requestWith:Request_Method_Post url:url params:params cacheTimeOut:cacheDays cacheKey:cacheKey success:successBlock error:errorBlock cache:cacheBlock];
}

#pragma mark - 【POST】文本数据请求-不使用缓存
+ (void)post:(NSString *)url params:(NSDictionary *)params success:(SuccessBlock)successBlock error:(ErorrBlock)errorBlock {
    [[ZHNetworkingManager instance] requestWith:Request_Method_Post url:url params:params cacheTimeOut:-1 cacheKey:nil success:successBlock error:errorBlock cache:nil];
}

#pragma mark - 【PUT】文本数据请求-不使用缓存
+ (void)put:(NSString *)url params:(NSDictionary *)params success:(SuccessBlock)successBlock error:(ErorrBlock)errorBlock {
    [[ZHNetworkingManager instance] requestWith:Request_Method_Put url:url params:params cacheTimeOut:-1 cacheKey:nil success:successBlock error:errorBlock cache:nil];
}

@end
